提醒:由於看到這系列鐵人訂閱人數漸漸變多,標記一下這些內容是在「非常萌新時期」所寫,更多技術內容請參考我的 Github,歡迎跟我一起討論 ^ ^
終於到最後一天啦!今天要學習使用 passport 來達成 使用者認證。其中提供了超過五百種 Strategies,包括可以使用獨立的使用者帳戶來進行認證,也可以使用第三方認證系統,如 Facebook、Twitter 等。在這裡我將學習使用 passport-local,來完成我的圖書館網站的認證系統。
Method:
serializeUser()
:可設定要將哪些 user 資訊,儲存在 Session 中的 passport.user。(如 user._id)deserializeUser()
:可藉由從 Session 中獲得的資訊去撈該 user 的資料。Middleware:
passport.initialize()
:確認 passport.user 是否已存在,若沒有則初始化一個空的。passport.session()
:用以處理 Session。若有找到 passport.user,則判定其通過驗證,並呼叫 deserializeUser()。passport.authenticate()
:用以驗證使用者。可設定要採用的 Strategy、驗證成功與失敗導向的頁面。ensureAuthenticated()
:客製化 middleware,用以確認其為「已驗證」的狀態,並導向頁面。設定 Strategies:
passport.use({設定參數},new <Strategies>(<verify callback>))
:首先先建立 Model,以設定儲存在 MongoDB 的資料結構。
models/user.js:
const mongoose = require('mongoose')
const Schema = mongoose.Schema
const UserSchema = new Schema({
username: { type: String, required: true, min: 6, index: true },
password: { type: String, required: true, min: 6 },
firstname: String,
lastname: String,
email: { type: String }
})
module.exports = mongoose.model('User', UserSchema)
接著將 passport 放進 app 的流程中。
app.js:
// 從user資料中撈ID
passport.serializeUser(function (user, done) {
done(null, user._id)
})
// 以ID去撈user資料
passport.deserializeUser(function (id, done) {
User.findById(id, function (err, user) {
done(err, user)
})
})
const LocalStrategy = require('passport-local').Strategy
const User = require('./models/user')
const flash = require('connect-flash')
const bcrypt = require('bcrypt-nodejs')
app.use(flash())
passport.use('login', new LocalStrategy({
passReqToCallback: true
},
(req, username, password, done) => {
User.findOne({ username: username }, (err, user) => {
const isValidPassword = (user, password) => {
return bcrypt.compareSync(password, user.password)
}
if (err) { return done(err) }
if (!user) { return done(null, false, req.flash('info', 'User not found.')) }
if (!isValidPassword(user, password)) { return done(null, false, req.flash('info', 'Invalid password')) }
return done(null, user) })}))
const passport = require('passport')
const LocalStrategy = require('passport-local').Strategy
const session = require('express-session')
app.use(session({
secret: 'secret',
saveUninitialized: true,
resave: false
}))
app.use(passport.initialize())
app.use(passport.session())
這部分跟前面的 Form 差不多。
以 views/login 為例:
extends layout
block content
h1=title
p=info
form(method='POST' action='')
div
label(for='username') Username:
input#username(type='text', placeholder='username' name='username' value=(undefined===user ? '' : user.username) required='true' autofocus)
label(for='password') Password:
input#password(type='password' name='password' required='true')
button(type='submit') Login
if errors
ul
for error in errors
li!= error.msg
將需要用到的 route 設置好。
routes/users.js:
const express = require('express')
const router = express.Router()
const userController = require('../controllers/userController')
router.get('/')
router.get('/signup', userController.user_signup_get)
router.post('/signup', userController.user_signup_post)
router.get('/login', userController.user_login_get)
router.post('/login', userController.user_login_post)
router.get('/logout', userController.user_logout)
router.get('/profile', userController.user_profile)
module.exports = router
最後再設定與 Route 相對應的 Controller
在 controllers/userController.js 中
以 user_login_post 為例:
const passport = require('passport')
// 採用前面設定 'login' 的 Strategy
exports.user_login_post = [
passport.authenticate('login', {
successRedirect: '/users/profile',
failureRedirect: '/users/login',
failureFlash: true
})]
參考:
[筆記] 透過 Passport.js 實作驗證機制
使用 Passport 實作 Nodejs 應用程式驗證機制
Node JS 如何用Passport.js 進行認證? let me show you!
NodeJS使用PassportJS處理認證流程
app.locals
:如此設定後,即可在所有的 view 中使用該變數。app.use((req, res, next) => {
app.locals.user = req.user
next()
})
logOut()
:這裡 passport 提供了一個 Function 可以在登出時,將 session 清空。(注意:雖然官網上寫logout()、logOut()都可,但我實作只有 logOut() 可以。)exports.user_logout = (req, res) => {
req.session.destroy(() => {
res.clearCookie('connect.sid')
res.redirect('/users/login')
})}
練習用的圖書館網站新增的功能持續更新中,也有放上我的 GitHub,可作參考。